package cn.tm.ms.nerver.idempotent.support;

import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

import org.apache.log4j.Logger;

import cn.tm.ms.nerver.idempotent.Idempotent;
import cn.tm.ms.nerver.idempotent.common.NamedThreadFactory;
import cn.tm.ms.nerver.idempotent.common.SystemClock;
import cn.tm.ms.nerver.idempotent.storage.IStorage;

/**
 * 幂等机制
 * 
 * @author lry
 */
public abstract class AbstractIdempotent implements Idempotent {

	// 日志输出
	protected final Logger logger = Logger.getLogger(AbstractIdempotent.class);

	/**
	 * 幂等校验时间窗,默认为60000毫秒
	 */
	public long timeWindow = 60000;

	// 清理定时器
	private ScheduledFuture<?> retryFuture;
	// 定时任务执行器
	private final ScheduledExecutorService retryExecutor = Executors
			.newScheduledThreadPool(1, new NamedThreadFactory("AbstractIdempotentCleanupTimer", true));
	
	/**
	 * 初始化(时间窗为60000ms)
	 * @param timeWindow
	 * @throws Throwable
	 */
	public void init() throws Throwable {
		this.init(timeWindow);
	}
	
	/**
	 * 初始化
	 * @param timeWindow
	 * @throws Throwable
	 */
	public void init(long timeWindow) throws Throwable {
		this.timeWindow=timeWindow;
		try {
			// 启动清洁工
			this.retryFuture = retryExecutor.scheduleWithFixedDelay(
					new Runnable() {
						public void run() {
							// 清理过期数据
							try {
								getStorage().cleanup();
							} catch (Throwable t) { // 防御性容错
								logger.error("Unexpected error occur at failed cleanup, cause: " + t.getMessage(), t);
							}
						}
					}, timeWindow, timeWindow, TimeUnit.MILLISECONDS);
		} catch (Throwable t) {
			t.printStackTrace();
		}
	}
	
	/**
	 * 校验
	 */
	public boolean check(String key) throws Throwable {
		boolean flag = this.getStorage().containsKey(key);
		long nowTime = SystemClock.now();

		if (flag) {// 校验
			long expireTime = this.getStorage().get(key) + timeWindow;// 到期时间
			if (nowTime <= expireTime) {// 未过期
				return false;
			}
		}

		this.getStorage().put(key, nowTime);// 更新
		return true;
	}

	/**
	 * 销毁
	 */
	public void destroy() {
		try {
			if(retryFuture!=null){
				retryFuture.cancel(true);
			}
		} catch (Throwable t) {
			logger.warn(t.getMessage(), t);
		}
	}
	
	

	public Future<?> getRetryFuture() {
		return retryFuture;
	}

	// 持久化
	protected abstract IStorage getStorage();

}
